#include <unistd.h>
#include "DepLibUV.hpp"
#include "common/HiCommonLog.hpp"

class DepLibUV_Scope {
public:
	DepLibUV_Scope() {
		int ret = DepLibUV::ClassInit();
		if (0 == ret) {
			HI_DEBUG("DepLibUV::ClassInit success\n");
			/*
			if (DepLibUV::RunLoop()) {
				printf("DepLibUV::RunLoop success\n");
			} else {
				printf("DepLibUV::RunLoop failed\n");
			}*/
		} else {
			HI_DEBUG("DepLibUV::ClassInit failed\n");
		}
	}
	~DepLibUV_Scope() {
		HI_FUNC_DEBUG();
		DepLibUV::Stop();
		DepLibUV::ClassDestroy();
		HI_DEBUG("DepLibUV::ClassDestroy called\n");
		HI_DEBUG("wait release handle of libuv\n");
		sleep(3); 
	}
};

/* Static variables. */

static uv_loop_t *loop{nullptr};

/* Static methods. */
static bool isStarted = false;

static bool isRunning = false;

static uv_thread_t  runThread;

static DepLibUV_Scope  scope;

int DepLibUV::ClassInit() {
	HI_FUNC_DEBUG();
	if (isStarted) {
		return 0;
	}
	isStarted = true;
	::loop = new uv_loop_t;
	int err = uv_loop_init(::loop);
	if (err != 0) {
		HI_DEBUG("uv_loop_init failed\n");
		delete ::loop;
		::loop = nullptr;
	}
	return err;
}

void DepLibUV::ClassDestroy() {
	HI_FUNC_DEBUG();
	if (::loop == nullptr) {
		return;
	}
	isStarted = false;
}

static void OnRunThread(void* arg) {
	HI_DEBUG("uv_run  started\n");
	uv_run(::loop, UV_RUN_DEFAULT);
	HI_DEBUG("uv_run  finished\n");
	isRunning = false;
	
	delete ::loop;
	::loop = nullptr;
	isStarted = false;
	DepLibUV::ClassInit();
}

bool DepLibUV::RunLoop() {
	HI_FUNC_DEBUG();
	// This should never happen.
	if (::loop == nullptr) {
		HI_DEBUG("::loop == nullptr\n");
		return false;
	}
	
	if (isRunning) {
		HI_DEBUG("loop is running, ignore this call\n");
		return true;
	}
	
	
	isRunning = true; 
	int ret = uv_thread_create(&runThread, OnRunThread, nullptr);
	if (ret != 0) {
		HI_DEBUG("uv_thread_create failed, reason:%s\n", uv_strerror(ret));
	}
	return true;
}

void DepLibUV::Stop() {
	if (::loop != nullptr) {
		HI_DEBUG("uv_stop called\n");
		
		
		HI_DEBUG("----active loop handles----\n");
		uv_print_active_handles(DepLibUV::GetLoop(), stdout);
		HI_DEBUG("----all loop handles----\n");
		uv_print_all_handles(DepLibUV::GetLoop(), stdout);
		
		if (isRunning) {
			delete ::loop;
			::loop = nullptr;
			isStarted = false;
			return;
		}
	
		uv_stop(::loop);

		int ret = uv_loop_close(::loop);
		if (0 == ret) {
			HI_DEBUG("uv_loop_close called\n");
		} else {
			HI_DEBUG("uv_loop_close failed.error:%s\n", uv_strerror(ret));
		}
		
		delete ::loop;
		::loop = nullptr;
	}
	
	if (!isRunning) {
		return;
	}
	HI_DEBUG("uv_thread_join\n");
	uv_thread_join(&runThread);
	isRunning = false;
}
uv_loop_t *DepLibUV::GetLoop() { return ::loop; }

uint64_t DepLibUV::GetTime() { return uv_now(::loop); }

uint64_t DepLibUV::GetTimeMs()
{
  return static_cast<uint64_t>(uv_hrtime() / 1000000u);
}